<template>
  <div class="meta-upload" v-loading="isLoading">
    <el-upload
      v-if="multiple"
      class="upload-attachment multiple"
      action="/api/dataio/input_uploader/upload"
      name="upload_file"
      :data="{ save_dir: saveDir, event: 'None' }"
      :headers="{ token }"
      multiple
      :limit="10"
      :accept="accept"
      :before-upload="handleBeforeUpload"
      :on-exceed="handleExceed"
      :on-success="(file, _f, ls) => handleSuccess(file, meta, ls)"
      :on-error="(err) => handleError(err)"
      :on-remove="(file, uploadFiles) => handleRemove(file, undefined, uploadFiles)"
      :on-preview="(file) => handleFilePreview(file)"
      :file-list="fileList"
      :show-file-list="true"
    >
      <el-button size="small" type="primary">点击上传</el-button>
      <!-- <template #file="{ file, index }" v-if="meta.preview && fileList && fileList.length > 0">
        <div class="preview-item">
          <File :meta="meta" :path="file.url" />
          <div class="close" @click.stop="handleRemove(file, index)"
            ><Icon :size="16" icon="material-symbols:close"
          /></div>
        </div>
      </template> -->
      <template #tip>
        <div v-if="meta.hint" class="el-upload__tip">
          {{ meta.hint }}
        </div>
      </template>
    </el-upload>
    <el-upload
      v-else
      ref="singleUploadRef"
      class="upload-attachment single"
      action="/api/dataio/input_uploader/upload"
      name="upload_file"
      :multiple="false"
      :limit="1"
      :accept="accept"
      :data="{ save_dir: saveDir, event: 'None' }"
      :headers="{ token }"
      :before-upload="handleBeforeUpload"
      :on-exceed="handleExceedSingle"
      :on-success="(file) => handleSuccessSingle(file, meta)"
      :on-error="(err) => handleErrorSingle(err)"
      :on-remove="(file) => handleRemoveSingle(file, meta)"
      :on-preview="(file) => handleFilePreview(file)"
      :file-list="fileList"
      :show-file-list="meta && !meta.preview"
    >
      <template v-if="meta.preview && fileList && fileList.length > 0">
        <div class="preview-item" v-for="(fs, i) in fileList" :key="i">
          <File :meta="meta" :path="fs.url" />
          <div class="close" @click.stop="handleRemoveSingle(fs, meta)"
            ><Icon :size="16" icon="material-symbols:close"
          /></div>
        </div>
      </template>
      <el-button v-else size="small" type="primary">点击上传</el-button>
      <template #tip>
        <div v-if="!meta?.hint" class="el-upload__tip"> {{ meta.hint }} </div>
      </template>
    </el-upload>
    <div class="upload-spec" v-if="!multiple && spec">
      <el-input v-model="uploadForm.spec_name" placeholder="请输入保存文件名">
        <template #prepend>
          <el-button @click="dialogVisible = true">{{
            uploadForm.spec_dir ? `${uploadForm.spec_dir}/` : '选择保存路径'
          }}</el-button>
        </template>
      </el-input>
    </div>
    <Dialog
      v-if="dialogVisible"
      title="选择路径"
      v-model="dialogVisible"
      append-to-body
      width="40%"
      class="file-tree-dialog"
      @close="dialogClose"
    >
      <file-tree v-model="selectPath" />
      <template #footer>
        <span class="dialog-footer">
          <el-button @click="dialogVisible = false">取 消</el-button>
          <el-button type="primary" @click="sureSelect">确 定</el-button>
        </span>
      </template>
    </Dialog>
  </div>
</template>
<script>
import { doAjaxDownload } from '@/api/basic'
import { useCache } from '@/hooks/web/useCache'
import { Dialog } from '@/components/Dialog'
import { ElMessage } from 'element-plus'
import File from './File.vue'
import { genFileId } from 'element-plus/es/components/upload/index'
import debounce from 'lodash.debounce'
import { useBasicStore } from '@/store/modules/basic'
import { createImageViewer } from '@/components/ImageViewer'
import { FilePreview } from '../components/FilePreview'
const { wsCache } = useCache()
const basicStore = useBasicStore()

export default {
  name: 'MetaUpload',
  components: { Dialog, File },
  props: {
    // multiple=false && spec=false => String
    // multiple=true && spec=false => String
    // multiple=false && spec=true => Array<Object>
    // multiple=true && spec=true => Array<String>
    modelValue: {
      type: [Array, Object, String],
      default: ''
    },
    // file字段meta
    meta: {
      type: Object,
      default: () => ({})
    },
    // 是否上传多个文件，多个文件会在一个字段里，逗号隔开
    multiple: {
      type: Boolean,
      default: false
    },
    saveDir: {
      type: String,
      default: 'None'
    },
    // 是否显示修改路径
    spec: {
      type: Boolean,
      default: false
    },
    accept: {
      // 上传文件限制
      type: String,
      default: undefined
    },
    row: {
      type: Object,
      default: () => ({})
    },
    filenameField: {
      // 文件名追加填充字段
      type: String,
      default: undefined
    }
  },
  data() {
    return {
      uploadForm: {
        upload_url: '',
        spec_dir: '',
        spec_name: ''
      },
      selectPath: '',
      dialogVisible: false,
      token: wsCache.get('Admin-Token'),
      multipleSelects: [],
      isLoading: false
    }
  },
  computed: {
    form: {
      get: function () {
        if (typeof this.modelValue === 'string') {
          return this.modelValue.replace('null', '')
        }
        return this.modelValue
      },
      set: function (value) {
        this.$emit('update:modelValue', value)
      }
    },
    fileList() {
      const val =
        typeof this.modelValue === 'string' ? this.modelValue.replace('null', '') : this.modelValue
      if (this.multiple) {
        if (!val) {
          return []
        }
        const urls = Array.isArray(val) ? val : (val || '').split(',')
        return urls.map((url) => {
          if (url) {
            const names = url.split('/')
            return { name: names[names.length - 1], url }
          }
        })
      } else {
        const url = this.uploadForm?.upload_url || val || ''
        if (url && typeof url === 'string') {
          const names = url.split('/')
          return [{ name: names[names.length - 1], url }]
        }
      }
      return []
    }
  },
  watch: {
    form(val) {
      if (typeof val === 'string') {
        if (this.multiple) {
          this.uploadForm = val
        } else if (this.spec) {
          this.uploadForm = {
            upload_url: val,
            spec_dir: '',
            spec_name: ''
          }
        } else {
          this.uploadForm = val
        }
      } else {
        Object.assign(this.uploadForm, val)
      }
    },
    uploadForm: {
      handler(val) {
        this.$emit('update:modelValue', val)
      },
      deep: true
    }
  },
  methods: {
    handleFilePreview(file) {
      const path = file.url
      const paths = path.split(':')
      const icon = paths.length > 1 ? paths[0] : ''
      const url = paths[1] || paths[0]
      if (this.meta?.preview) {
        // 如果是图片，直接调用图片预览
        if (icon === 'image') {
          createImageViewer({
            urlList: [url]
          })
        } else {
          FilePreview({
            url
          })
        }
      } else {
        this.isLoading = true
        doAjaxDownload(url, {}, 'get')
          .then((res) => {
            let fileName = file.name
            const contentDisposition = res.headers['content-disposition']
            if (contentDisposition && contentDisposition.includes('filename')) {
              const filenameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/
              const matches = filenameRegex.exec(contentDisposition)
              if (matches != null && matches[1]) {
                const metchName = matches[1].replace(/['"]/g, '') // 去除引号
                fileName = decodeURIComponent(metchName.replace('utf-8', ''))
              }
            }
            const blob = new Blob([res.data])
            const downloadUrl = window.URL.createObjectURL(blob)
            const a = document.createElement('a')
            a.style.display = 'none'
            a.href = downloadUrl
            a.download = fileName // 设置下载文件的默认名
            document.body.appendChild(a)
            a.click()
            window.URL.revokeObjectURL(downloadUrl) // 释放 URL 对象
            document.body.removeChild(a)
          })
          .finally(() => {
            this.isLoading = false
          })
      }
    },
    handleBeforeUpload(file) {
      const fileExtend = file.name.split('.').reverse[0]
      const acceptExtends = [this.meta?.props?.acceptExtends].flat().filter((x) => !!x)
      if (!acceptExtends.every((x) => new RegExp(x).test(fileExtend))) {
        const acceptMessage = this.meta?.props?.acceptMessage
        if (acceptMessage) {
          ElMessage.warning(acceptMessage)
        }
        return false
      }
      basicStore.toggleLoading(true)
    },
    handleExceedSingle(files, fileList) {
      this.$refs.singleUploadRef?.clearFiles()
      const file = files[0]
      file.uid = genFileId()
      this.$refs.singleUploadRef?.handleStart(file)
      this.$refs.singleUploadRef?.submit()
    },
    handleSuccessSingle(res, file) {
      basicStore.toggleLoading(false)
      if (res.error_code !== 200) {
        this.$message({ type: 'warning', duration: 8000, message: res.error_msg })
        this.$refs.singleUploadRef?.handleRemove(file)
        return Promise.reject()
      }
      if (this.spec) {
        this.uploadForm.upload_url = res.file
      } else {
        // col.value = res.file
        this.$emit('update:modelValue', res.file)
      }
      if (this.filenameField && this.row) {
        // eslint-disable-next-line vue/no-mutating-props
        this.row[this.filenameField] = res.file.split('/').reverse()[0]
      }
      this.$emit('change', res.file)
    },
    handleErrorSingle(err) {
      basicStore.toggleLoading(false)
      this.$message(`上传失败，${err}`)
    },
    handleRemoveSingle(file, col) {
      if (this.spec) {
        this.uploadForm.upload_url = ''
      } else {
        // col.value = ''
        this.$emit('update:modelValue', '')
      }
      this.$emit('change')
    },
    handleExceed(files, fileList) {
      this.$message.warning(
        `当前限制选择 10 个文件，本次选择了 ${files.length} 个文件，共选择了 ${
          files.length + fileList.length
        } 个文件`
      )
    },
    handleSuccess(res, col, list) {
      basicStore.toggleLoading(false)
      if (res.error_code !== 200) {
        this.$message({ type: 'warning', duration: 8000, message: res.error_msg })
      }
      this.multipleSelects.push(res.file)
      this.debounceEmit(col, list)
      this.$emit('change', this.multipleSelects)
    },
    handleError(err) {
      basicStore.toggleLoading(false)
      this.$message(`上传失败，${err}`)
    },
    handleRemove(res, index, uploadFiles) {
      if (this.multiple) {
        let result
        if (typeof index === 'number') {
          result = this.fileList.filter((_x, idx) => idx != index).map((x) => x.url)
        } else {
          result = uploadFiles.map((x) => x.url)
        }
        this.$emit(
          'update:modelValue',
          typeof this.modelValue === 'string' ? result.join(',') : result
        )
        this.$emit('change', this.multipleSelects)
      }
      console.log(this.fileList)
    },
    debounceEmit: debounce(function (col, list) {
      const val = `${col.value}`
      const result = list
        .map((x) => x.url || x.response?.file)
        .filter((x) => !!x)
        .join(',')
      if (this.filenameField && this.row) {
        const resultName = list
          .map((x) => (x.url || x.response?.file).split('/').reverse()[0])
          .filter((x) => !!x)
          .join(',')
        // eslint-disable-next-line vue/no-mutating-props
        this.row[this.filenameField] = resultName
      }
      this.$emit('update:modelValue', result)
      this.multipleSelects = []
    }, 200),
    sureSelect() {
      if (this.selectPath) {
        this.uploadForm.spec_dir = this.selectPath
        this.dialogVisible = false
      }
    },
    getFileList(col) {
      const url = this.uploadForm.upload_url || this.modelValue || ''
      if (url) {
        const names = url.split('/')
        return [{ name: names[names.length - 1], url }]
      }
      return []
    },
    dialogClose() {}
  }
}
</script>

<style scoped lang="less">
.meta-upload {
  width: 100%;

  .upload-spec {
    display: flex;
    align-items: center;
  }

  :deep(.upload-attachment) {
    min-height: 39px;
    .el-upload--text {
      flex-wrap: wrap;
      justify-content: flex-start;
    }
    .el-upload__tip {
      margin-top: 0;
      margin-left: 7px;
    }

    .el-upload-list {
      flex: 0 0 400px;
    }

    &.multiple {
      width: 400px;
    }

    &.single {
      display: flex;
      align-items: center;
    }
  }

  .preview-item {
    position: relative;
    margin-left: 8px;

    &:hover {
      .close {
        display: inline-block;
      }
    }

    .close {
      display: none;
      position: absolute;
      right: -5px;
      top: -5px;
      font-size: 16px;
      cursor: pointer;
      background: #ccc;
      color: #fff;
      width: 16px;
      height: 16px;
      line-height: 16px;
      text-align: center;
      border-radius: 100%;

      &:hover {
        background-color: #aaa;
      }
    }
  }
}
</style>
<style lang="less">
.file-tree-dialog {
  .el-scrollbar__wrap {
    height: 100%;

    .el-scrollbar__view {
      height: 100%;
    }
  }
}
</style>
